#include <algorithm>

#include "rle.hpp"


void RLEncoder::reset()
{
	buffer_.clear();
	output_.clear();
}

void RLEncoder::charge(const void *data, int length)
{
	const Type *cdata = static_cast<const Type *>(data);
	
	buffer_.reserve(buffer_.size() + length);
	std::copy(cdata, cdata + length, std::back_inserter(buffer_));
}

void RLEncoder::commitEncode()
{
	output_.clear();

	if (buffer_.empty())
		return;
	
	for (int i = 0, i_end = buffer_.size(); i != i_end; ) {
		int j, j_end;
		// count equal symbols
		for (j = i, j_end = std::min(i_end, i + TYPE_MAX - 1); j != j_end && buffer_[i] == buffer_[j]; ++j);
//		if (j >= i + TYPE_MAX)
//			j = i + TYPE_MAX - 1;
		// at least two
		if (j > i) {
			output_.push_back(j - i + TYPE_MAX);
			output_.push_back(buffer_[i]);
			i = j;
		}
		else {
			output_.push_back(0);
			Type *lenPlace = &output_.back();
			// count unequal symbols
			for (j = i; j != i_end - 1 && buffer_[j] != buffer_[j + 1]; ++j) {
				output_.push_back(buffer_[j]);
			}
			if (j == i_end - 1) {
				output_.push_back(buffer_[j]);
				++j;
			}
			if (j >= i + TYPE_MAX)
				j = i + TYPE_MAX - 1;
			*lenPlace = j - i;
			i = j;
		}
	}
}

void RLEncoder::commitDecode()
{
	output_.clear();
	
	if (buffer_.empty())
		return;
	
	for (int i = 0, i_end = buffer_.size(); i != i_end;) {
		if (buffer_[i] < TYPE_MAX) {
			// different values
			if (i + buffer_[i] > i_end)
				throw Exception();
			std::copy(buffer_.begin() + i + 1, buffer_.begin() + i + 1 + buffer_[i],
				std::back_inserter(output_));
			i += buffer_[i] + 1;
		}
		else{
			// equal values
			if (i + 2 > i_end)
				throw Exception();
			std::fill_n(std::back_inserter(output_),
				buffer_[i] - TYPE_MAX, buffer_[i + 1]);
			i += 2;
		}
	}
}

int RLEncoder::getLength() const
{
	return output_.size();
}

const void *RLEncoder::getBuffer() const
{
	return &output_[0];
}
